<?php
class VideoInfrastructure {
	const STATUS_NEW = 0;
	const STATUS_ACTIVE = 1;
	const STATUS_DELETED = -1;
	
	static public function GetWritableRecordFactory() {
		$config = ConfigReaderFactory::getInstance()->getConfigReader(VideoInfrastructureServerConfig::CONFIG_FILE)->getConfig();
		$db = $config->db->writer;
		$user = $config->db->user;
		$pass = $config->db->pwd;
		$name = $config->db->name;
		return new WritableRecordFactory($db, $user, $pass, $name);
	}

	static public function GetReadableRecordFactory() {
		$config = ConfigReaderFactory::getInstance()->getConfigReader(VideoInfrastructureServerConfig::CONFIG_FILE)->getConfig();
		$readers = array();
		foreach ($config->db->readers->reader as $reader) {
			$readers[] = (string) $reader;
		}
		$i = array_rand($readers);

		$db = $readers[$i];
		$user = $config->db->user;
		$pass = $config->db->pwd;
		$name = $config->db->name;
		return new ReadableRecordFactory($db, $user, $pass, $name);
	}

	static public function CreateSource($user_id) {
		// write source record
		$rf = self::GetWritableRecordFactory();
		$source_entry = new Source();		
		$source_entry->creator = $user_id;
		$source_entry->create_time = date('c');
		$source_entry->modifier = $user_id;
		$source_entry->id = $rf->insert($source_entry);
		
		if (!$source_entry->id) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Create source record FAILS");
			error_log("Parameters: " . var_export($source_entry, TRUE));
			
			return -1; // create source record fails
		}
		
		return $source_entry->id;
	}

	static public function Upload($user_id, $source_id, array $segment_files) {
		$rf = self::GetWritableRecordFactory();			
		
		$segment_entry_list = $rf->find('Segment', 
			'source_id=?',
			array($source_id)
		);
		
		if (count($segment_entry_list) > 0) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR: Cannot upload files to a source with segment files. source_id = {$source_id}");
			
			return -7; // Cannot upload files to a source with segment files
		}
	
		$total_segment = count($segment_files);
		if ( !is_array($segment_files) || ($total_segment == 0) ) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - No uploaded files");
			
			return -6; // No uploaded file
		}
			
		$source_files = self::GetUploadStorage($source_id, $total_segment);
		foreach ($segment_files as $segment_no => $segment_file) {
			$video_info = VideoHelper::GetVideoInformation($segment_file['file']);
			if (!$video_info) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Invalid video file: {$segment_file['file']}");			
								
				return -1;	// non-video
			}
			
			$dir = dirname($source_files[$segment_no]);
			$ret = @mkdir($dir, 0777, TRUE);
			$ret = @chmod($dir, 0777);
			$ret = rename($segment_file['file'], $source_files[$segment_no]);

			if (!$ret) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Move uploaded files FAILS");			
				error_log("Parameters: source: {$segment_file['file']}, destination: {$source_files[$segment_no]} ");
				
				return -5; // Move uploaded files FAILS
			}
			
			$segment_file['file'] = $source_files[$segment_no];
			$segment_file['info'] = $video_info;
			$segment_files[$segment_no] = $segment_file;
		}
		
		// Find Source
		$source_entry = $rf->findInstance('Source', $source_id);
		if (!$source_entry) {
			return -2;	// no-source
		}
		
		// validate video
		$total_duration = 0;
		$breakpoints = array();
		
		foreach ($segment_files as $segment_no => $segment_file) {
			$video_info = $segment_file['info'];
			$duration = $video_info['Video']['Duration'] / 1000;

			// write source record
			$segment_entry = new Segment();
			$segment_entry->source_id = $source_id;
			$segment_entry->segment_no = $segment_no;
			$segment_entry->version = 1;
			$segment_entry->farm = self::GetCurrentFarm();
			$segment_entry->original_name = $segment_file['name'];
			$segment_entry->subtitled = 1; // TODO: How to pass
			$segment_entry->duration = $duration;
			$segment_entry->width = (int) $video_info['Video']['Width'];
			$segment_entry->height = (int) $video_info['Video']['Height'];
			$segment_entry->video_codec = $video_info['Video']['Codec/Family'];
			$segment_entry->video_bitrate = $video_info['Video']['BitRate'];
			$segment_entry->frame_rate = $video_info['Video']['FrameRate'];
			$segment_entry->pixel_aspect_ratio = $video_info['Video']['PixelAspectRatio'];
			$segment_entry->audio_codec = $video_info['Audio']['Codec/Family'];
			$segment_entry->audio_bitrate = $video_info['Audio']['BitRate'];
			$segment_entry->audio_channel = $video_info['Audio']['Channel(s)'];
			$segment_entry->audio_sample_rate = $video_info['Audio']['SamplingRate'];			
			$segment_entry->status = 1;
			$segment_entry->creator = $user_id;
			$segment_entry->create_time = date('c');
			$segment_entry->modifier = $user_id;
			$segment_entry->id = $rf->insert($segment_entry);
			if (!$segment_entry->id) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Create segment record FAILS");
				error_log("Parameters: " . var_export($segment_entry, TRUE));
				
				return -3; // create export record fails
			}
			
			$total_duration += $duration;
			$breakpoints[] = $total_duration;
		}
		array_pop($breakpoints);

		// Update source
		$source_entry->duration = $total_duration;
		$source_entry->breakpoint = implode(",", $breakpoints);
		$source_entry->status = self::STATUS_ACTIVE;
		
		$ret = $rf->save_by_fields($source_entry, array('id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update source FAILS");
			error_log("Parameters: " . var_export($source_entry, TRUE));
			return -4; // update source fails
		}
		
		return $source_entry->id;
	}

	
	static public function GetSource($source_id) {
		$rf = self::GetWritableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);		
	
		return $source_entry;
	}
	
	static public function SetBreakpoint($source_id, $breakpoint) {
		$rf = self::GetWritableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);
		$source_entry->breakpoint = $breakpoint;
		
		$ret = $rf->save_by_fields($source_entry, array('id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Set breakpoint FAILS");
			error_log("Parameters: " . var_export($source_entry, TRUE));			
		}
		
		return $ret;
	}
	
	static public function AddMeta($source_id, $login_id, $title, $person, $tag, array $miscellaneous) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);
		$found = $rf->find('Meta', 'source_id=?', array($source_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new Meta();
			$meta_entry->source_id = $source_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// add meta
		$meta_entry->addValueToJsonArray('title', $title);
		$meta_entry->addValueToJsonArray('person', $person);
		$meta_entry->addValueToJsonArray('tag', $tag);
		$meta_entry->addHashToJsonHash('miscellaneous', $miscellaneous);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Add meta FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}

	static public function UpdateMeta($source_id, $login_id, $title, $person, $tag, array $miscellaneous) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);
		$found = $rf->find('Meta', 'source_id=?', array($source_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
			$update = TRUE;
		} else {
			$meta_entry = new Meta();
			$meta_entry->source_id = $source_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta		
		$meta_entry->title = json_encode($title);
		$meta_entry->person = json_encode($person);
		$meta_entry->tag = json_encode($tag);
		$meta_entry->miscellaneous = json_encode($miscellaneous);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}

	static public function RequestExport($login_id, $source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $callback_url, $format_id = 1) {
		return self::RequestDistribute($login_id, $source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, 0, $callback_url, $format_id);
		
	}

	static public function GetExport($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id = 1) {
		$rf = self::GetReadableRecordFactory();

		// check export record
		$found = $rf->find(
			'Export',
			'source_id=? AND width=? AND height=? AND video_codec=? AND video_bitrate=? AND frame_rate=? AND pixel_aspect_ratio=? AND audio_codec=? AND audio_bitrate=? AND audio_channel=? AND audio_sample_rate=? AND export_arg=? AND format_id=?',
			array($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id)
		);

		$export_entry = array_shift($found);

		return $export_entry;
	}
	
	static public function GetExportById($exportId) {
		$rf = self::GetWritableRecordFactory();
		
		$export_entry = $rf->findInstance('Export', $exportId);		
		
		return $export_entry;
	}
	
	static public function RequestDistribute($login_id, $source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $destination_id, $callback_url, $format_id = 1) {
		$rf = self::GetWritableRecordFactory();
		
		if (($width <= 0) || ($height <= 0) || ($source_id <= 0)) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Invalid input parameters FAILS");
			error_log("Parameters: width={$width}, height={$height}, source_id={$source_id}");
			
			return -1;
		}

		// check existing export
		$export_entry = self::GetExport($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id);
				
		if (!$export_entry) {
			// new export entry
			$export_entry = new Export();
			$export_entry->source_id = $source_id;
			$export_entry->width = $width;
			$export_entry->height = $height;
			$export_entry->video_codec = $video_codec;
			$export_entry->video_bitrate = $video_bitrate;
			$export_entry->frame_rate = $frame_rate;
			$export_entry->pixel_aspect_ratio = $pixel_aspect_ratio;
			$export_entry->audio_codec = $audio_codec;
			$export_entry->audio_bitrate = $audio_bitrate;
			$export_entry->audio_channel = $audio_channel;
			$export_entry->audio_sample_rate = $audio_sample_rate;
			$export_entry->export_arg = $export_arg;
			$export_entry->timestamp = time();
			$export_entry->farm = self::GetCurrentFarm();
			$export_entry->ready = self::STATUS_NEW;
			$export_entry->version = 1;
			$export_entry->format_id = $format_id;
			$export_entry->id = $rf->insert($export_entry);
			
			if (!$export_entry->id) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Create export record FAILS");
				error_log("Parameters: " . var_export($export_entry, TRUE));
				
				return -1; // create export record fails
			}
		}
		
		if ($export_entry->ready == self::STATUS_ACTIVE) {
			if ($destination_id > 0) {
				$export_id = $export_entry->id;
				$ret = VideoInfrastructure::QueueDistribute($login_id, $export_id, $destination_id, $callback_url);
				if ($ret !== TRUE) {
					error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
					error_log("Error - Query Distribute Fails: export_id = {$export_id}, ret = {$ret}");
					error_log("Parameters: login_id = {$login_id}, export_id = {$export_id}, destination_id = {$destination_id}, callback_url = {$callback_url}");
					return -3; // Insert distribution queue fails
				}
			}
		} else {
			// enter export queue
			list($broker, $queue) = self::GetExportQueueInfo();
			$enqueue_ret = MessageHelper::sendMessage($broker, $queue, array(
				'login_id' => $login_id,
				'export_id' => $export_entry->id,
				'destination_id' => $destination_id,
				'callback_url' => $callback_url
			));
			
			if (!$enqueue_ret) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Insert export queue FAILS");
				$parameters = array($broker, $queue, array(
						'login_id' => $login_id,
						'export_id' => $export_entry->id,
						'destination_id' => $destination_id,
						'callback_url' => $callback_url
					)
				);			
				error_log("Parameters: " . var_export($parameters, TRUE));
				
				return -2; // Insert export queue FAILS
			}
		}
	
		
		return $export_entry->id;
	}

	static public function QueueDistribute($login_id, $export_id, $destination_id, $callback_url) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$export_entry = $rf->findInstance('Export', $export_id);
		if (!$export_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Export entry not found: export_id = {$export_id}");
			
			return -1; //Export entry not found
		}
		
		$destination_entry = $rf->findInstance('Destination', $destination_id);
		if (!$destination_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Destination entry not found: destination_id = {$destination_id}");
			
			return -2; //Destination entry
		}
		
		$found = $rf->find('Distribution',
			'export_id=? AND destination_id=?',
			array($export_id, $destination_id)
		);

		if ($found) {
			$distribution_entry = array_shift($found);			
		} else {
			// create new distribution record
			$distribution_entry = new Distribution();
			$distribution_entry->export_id = $export_id;
			$distribution_entry->destination_id = $destination_id;
			$distribution_entry->distributed_by = $login_id;
			$distribution_entry->status = 0;
			$distribution_entry->id = $rf->insert($distribution_entry);
			if ($distribution_entry->id != 0) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Create distribution record FAILS");
				error_log("Parameters: " . var_export($distribution_entry, TRUE));
				
				return -3; // Create destination record FAILS
			}
		} 

		// enter distribution queue
		if ($destination_entry->broker) {
			$broker = $destination_entry->broker;
			$queue = $destination_entry->queue;
			$ret = MessageHelper::sendMessage($broker, $queue,
				array('export_id' => $export_id, 'callback_url' => $callback_url)
			);
				
			if (!$ret) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - sendMessage FAILS");
				error_log("MessageHelper::sendMessage({$broker}, {$queue}, array('export_id' => {$export_id}, 'callback_url' => {$callback_url})");
				
				return -4; // Send Message FAILS
			}
		}
		
		return TRUE;
	}

	static public function Export($export_id, $is_reexport = FALSE) {
		$rf = self::GetWritableRecordFactory();
		
		// get db records		
		$export_entry = $rf->findInstance('Export', $export_id);
		$source_entry = $rf->findInstance('Source', $export_entry->source_id);
		
		if (!is_object($export_entry)) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR: Export {$export_id} is missing in DB");
			
			return -1;
		}

		// skip exporting if its been done
		if (!$is_reexport && $export_entry->ready) {
			return TRUE;
		}
		
		if ($source_entry->status != self::STATUS_ACTIVE) {
			return -6;
		}
		
		$segment_entry_list = $rf->find('Segment', 
			'source_id=?',
			array($export_entry->source_id)
		);
		
		if (count($segment_entry_list) == 0) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR: No segment found. source_id = {$export_entry->source_id}");
			
			return -2;
		}

		$video_files = array();
		foreach ($segment_entry_list as $segment_entry) {
			$file = self::GetSegmentStorage($segment_entry->source_id, $segment_entry->farm, $segment_entry->segment_no);
			if (!file_exists($file)) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR: Missing segment file: {$file}");
				
				return -3;
			}
			
			$video_files[] = $file;
		}
						
		// export video
		$export_file = self::GetExportedStorage($export_id, $export_entry->farm, $export_entry->format_id);
		$dir = dirname($export_file);
		$ret = @mkdir($dir, 0777, true);
		$ret = @chmod($dir, 0777);
		
//var_dump($video_files);
		// Hard code to use MP4Encoder now
		//$encoder = new MP4Encoder();
		$encoder = EncoderFactory::getInstance()->getEncoder($export_entry->format_id);
		if (!is_object($encoder)) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Export format not supported");
			error_log("Parameters: " . var_export($export_entry, TRUE));
			
			return -6;
		}
		
		$data = array(
			'video_files' => $video_files,
			'output_file' => $export_file,
			'width' => $export_entry->width,
			'height' => $export_entry->height,
			'video_codec' => $export_entry->video_codec,
			'video_bitrate' => $export_entry->video_bitrate,
			'frame_rate' => $export_entry->frame_rate,
			'pixel_aspect_ratio' => $export_entry->pixel_aspect_ratio,
			'audio_codec' => $export_entry->audio_codec,
			'audio_bitrate' => $export_entry->audio_bitrate,
			'audio_channel' => $export_entry->audio_channel,
			'audio_sample_rate' => $export_entry->audio_sample_rate,
			'export_arg' => $export_entry->export_arg,
		);
		$ret = $encoder->encode($data);
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - encode FAILS");
			error_log("Parameters: " . var_export($data, TRUE));
			
			return -4;
		}

		// update db record		
		$export_entry->ready = 1;
		$ret = $rf->save($export_entry);
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update export_entry FAILS");
			error_log("Parameters: " . var_export($export_entry, TRUE));
			
			return -5;
		}
		
		return TRUE;
	}

	static public function QueryStatusTroublesomely($source_id, $destination_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id = 1) {
		$export_entry = self::GetExport($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id);

		if (!is_object($export_entry)) {
			return FALSE; // return "No export record found";
		}
		
		return self::QueryStatusEasily($export_entry->id, $destination_id);
	}

	static public function QueryStatusEasily($export_id, $destination_id) {
		$rf = self::GetReadableRecordFactory();

		// check export record
		$export_entry = $rf->findInstance('Export', $export_id);
		if (!$export_entry) {
			return -91; // return "No export record found"
		}
		
		// check export status
		$status_code = $export_entry->ready;
		if ($status_code != '1') {
			return 2;// return "Exporting"
		}
		
		$distributor = DistributorFactory::getInstance()->getDistributor($destination_id);
		if (!is_object($distributor)) {
			return -99; // Error: no distributor
		}
		return $distributor->getVideo($export_id);
	}

	static public function GetExportQueueInfo() {
		$config = ConfigReaderFactory::getInstance()->getConfigReader(VideoInfrastructureServerConfig::CONFIG_FILE)->getConfig();
		$broker = $config->export->broker;
		$queue = $config->export->queue;

		return array($broker, $queue);
	}
	
	static public function GetCurrentFarm() {
		$config = ConfigReaderFactory::getInstance()->getConfigReader(VideoInfrastructureServerConfig::CONFIG_FILE)->getConfig();
		return $config->videoFarm;
	}

	static public function GetUploadStorage($id, $total_segment) {
		$farm_id = self::GetCurrentFarm();
		$hash = ceil($id / 100);
		$dir = "/data/video_infrastructure/video/$farm_id/source/$hash/$id";
		
		$segment_files = array();
		for ($i = 1; $i <= $total_segment; $i++) {
			$filename = sprintf("%012d.mp4", $i);
			$segment_files[$i] = "$dir/$filename";
		}
		
		return $segment_files;
	}

	static public function GetSegmentStorage($source_id, $farm_id, $segment_no) {		
		$hash = ceil($source_id / 100);
		$dir = "/data/video_infrastructure/video/$farm_id/source/$hash/$source_id";
		$filename = sprintf("%012d.mp4", $segment_no);
		
		return "$dir/$filename";
	}
	
	static public function GetExportedStorage($id, $farm_id, $format_id) {
		$encoder = EncoderFactory::getInstance()->getEncoder($format_id);
		if (!is_object($encoder)) {
			return -1;
		}
		$ext = $encoder->getFormat();
		
		$hash = ceil($id / 100);
		$dir = "/data/video_infrastructure/video/$farm_id/export/$hash";
		$filename = sprintf("%012d.{$ext}", $id);
		return "$dir/$filename";
	}

	static public function GetExportVideoUrlTroublesomely($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id = 1) {
		$export_entry = self::GetExport($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id);

		if (!is_object($export_entry)) {
			return FALSE; // return "No export record found";
		}
		
		return self::GetExportVideoUrl($export_entry->id, $export_entry->farm);
	}
	
	static public function GetExportVideoUrlEasily($export_id) {
		$rf = self::GetReadableRecordFactory();
		$export_entry = $rf->findInstance('Export', $export_id);
		if (!$export_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Export entry not found: export_id = {$export_id}");
			
			return -1; //Export entry not found
		}

		// check export status
		$status_code = $export_entry->ready;
		switch ($status_code) {
		case self::STATUS_ACTIVE:
			return self::GetExportVideoUrl($export_entry->id, $export_entry->farm);
		
		case self::STATUS_DELETED:
			return self::STATUS_DELETED;
		
		default:
			return 2; //"Exporting"
		}		
	}
	
	static public function GetExportVideoUrl($id, $farm_id) {
		$rf = self::GetReadableRecordFactory();
		$export_entry = $rf->findInstance('Export', $id);
		$file = self::GetExportedStorage($id, $farm_id, $export_entry->format_id);
		$url = str_replace('/data/video_infrastructure', 'http://vi.tvb.com', $file);
		
		return $url;
	}
		
	static public function Callback($export_id, $destination_id, $status, $callback_url) {
		if (!$callback_url) {
			return TRUE;
		}

		$information = self::QueryStatusEasily($export_id, $destination_id);
		
		$request = new HttpRequest($callback_url, HttpRequest::METH_POST);
		$key = ConfigReaderFactory::getInstance()->getConfigReader(VideoInfrastructureServerConfig::WEB_SERVICE_CONFIG_FILE)->getConfig()->webservice->key;
		$signature = sha1($key.$export_id);
		$parameters = array(
			'export_id' => $export_id,
			'destination_id' => $destination_id,
			'status' => $status,			
			'signature' => $signature,
			'information' => $information,
		);		
		$request->addPostFields($parameters);
		
		try {
			$ret = $request->send();
		} catch (Exception $e) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Callback FAILS: callback_url = {$callback_url}");
			error_log("Parameters: " . var_export($parameters, TRUE));
			error_log($e->getMessage());
			error_log($e->getTraceAsString());
			
			return -1;
		}
		
		
		return TRUE;
	}

	static public function UpdateAcl($source_id, $destination_id, $login_id, $start_time, $end_time, array $country_allow_list, array $country_deny_list) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('Acl', 'source_id=? AND destination_id=?', array($source_id, $destination_id));

		// create new acl record when necessary
		if ($found) {
			$acl_entry = array_shift($found);
		} else {
			$acl_entry = new Acl();
			$acl_entry->source_id = $source_id;
			$acl_entry->destination_id = $destination_id;
			$acl_entry->create_time = date('c');
			$acl_entry->creator = $login_id; 
		}

		// update acl
		$acl_entry->start_time = date('c', $start_time);
		$acl_entry->end_time = date('c', $end_time);
		$acl_entry->country_allow_list = json_encode($country_allow_list);
		$acl_entry->country_deny_list = json_encode($country_deny_list);
		$acl_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($acl_entry, array('source_id', 'destination_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update acl FAILS");
			error_log("Parameters: " . var_export($acl_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function UpdateMetaManual($source_id, $login_id, array $manual_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaManual', 'source_id=?', array($source_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaManual();
			$meta_entry->source_id = $source_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->manual_tags = json_encode($manual_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-manual FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaManual($source_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaManual', 'source_id=?', array($source_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-manual entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function UpdateMetaArtiste($source_id, $artiste_id, $login_id, array $system_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaArtiste', 'source_id=? AND artiste_id=?', array($source_id, $artiste_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaArtiste();
			$meta_entry->source_id = $source_id;
			$meta_entry->artiste_id = $artiste_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->system_tags = json_encode($system_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'artiste_id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-artiste FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaArtiste($source_id, $artiste_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaArtiste', 'source_id=? AND artiste_id=?', array($source_id, $artiste_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'artiste_id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-artiste entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function UpdateMetaBFarm($source_id, $blog_id, $login_id, array $system_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaBFarm', 'source_id=? AND blog_id=?', array($source_id, $blog_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaBFarm();
			$meta_entry->source_id = $source_id;
			$meta_entry->blog_id = $blog_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->system_tags = json_encode($system_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'blog_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-bfarm FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaBFarm($source_id, $blog_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaBFarm', 'source_id=? AND blog_id=?', array($source_id, $blog_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'blog_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-bfarm entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function UpdateMetaBlog($source_id, $blog_id, $login_id, array $system_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaBlog', 'source_id=? AND blog_id=?', array($source_id, $blog_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaBlog();
			$meta_entry->source_id = $source_id;
			$meta_entry->blog_id = $blog_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->system_tags = json_encode($system_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'blog_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-blog FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaBlog($source_id, $blog_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaBlog', 'source_id=? AND blog_id=?', array($source_id, $blog_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'blog_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-blog entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function UpdateMetaCast($source_id, $cast_id, $login_id, array $system_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaCast', 'source_id=? AND cast_id=?', array($source_id, $cast_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaCast();
			$meta_entry->source_id = $source_id;
			$meta_entry->cast_id = $cast_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->system_tags = json_encode($system_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'cast_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-cast FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaCast($source_id, $cast_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaCast', 'source_id=? AND cast_id=?', array($source_id, $blog_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'cast_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-cast entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function UpdateMetaProgramme($source_id, $programme_id, $login_id, array $system_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaProgramme', 'source_id=? AND programme_id=?', array($source_id, $programme_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaProgramme();
			$meta_entry->source_id = $source_id;
			$meta_entry->programme_id = $programme_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->system_tags = json_encode($system_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'programme_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-programme FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaProgramme($source_id, $programme_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaProgramme', 'source_id=? AND programme_id=?', array($source_id, $programme_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'programme_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-programme entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function UpdateMetaEpisode($source_id, $episode_id, $login_id, array $system_tags) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaEpisode', 'source_id=? AND episode_id=?', array($source_id, $episode_id));

		// create new meta record when necessary
		if ($found) {
			$meta_entry = array_shift($found);
		} else {
			$meta_entry = new MetaEpisode();
			$meta_entry->source_id = $source_id;
			$meta_entry->episode_id = $episode_id;
			$meta_entry->create_time = date('c');
			$meta_entry->creator = $login_id; 
		}

		// update meta
		$meta_entry->system_tags = json_encode($system_tags);
		$meta_entry->modifier = $login_id;

		// save
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'episode_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Update meta-episode FAILS");
			error_log("Parameters: " . var_export($meta_entry, TRUE));
		}
		
		return $ret;
	}
	
	static public function DeleteMetaEpisode($source_id, $episode_id, $login_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$found = $rf->find('MetaProgramme', 'source_id=? AND episode_id=?', array($source_id, $episode_id));

		if (!$found) {
			return TRUE;
		}
		$meta_entry = array_shift($found);

		// update meta
		$meta_entry->modifier = $login_id;
		$meta_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($meta_entry, array('source_id', 'episode_id'));		
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted meta-episode entry record FAILS");
			error_log("Parameters: " . var_export($meta_entry));
			
			return -1;
		}
		
		return TRUE;
	}
	
	static public function DeleteSource($source_id) {
		// get db records
		$rf = self::GetWritableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);
	
		if (!$source_entry) {
			return TRUE; // no source is found
		}
		
		$source_entry->status = self::STATUS_DELETED;
		$ret = $rf->save($source_entry);
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted source entry record FAILS");
			error_log("Parameters: " . var_export($source_entry));
			
			return -1;
		}
		
		
		$export_entry_list = $rf->find('Export', 'source_id=?', array($source_id));
		foreach ($export_entry_list as $export_entry) {
			$export_entry->ready = self::STATUS_DELETED;
			$ret = $rf->save($export_entry);
			if (!$ret) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Mark-Deleted export entry record FAILS");
				error_log("Parameters: " . var_export($export_entry));
				
				return -2;
			}
			
			$distribution_entry_list = $rf->find('Distribution', 'export_id=?', array($export_entry->id));
			foreach ($distribution_entry_list as $distribution_entry) {
				$distribution_entry->status = self::STATUS_DELETED;
				$ret = $rf->save_by_fields($distribution_entry, array('export_id', 'destination_id'));
				if (!$ret) {
					error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
					error_log("ERROR - Mark-Deleted distribution entry record FAILS");
					error_log("Parameters: " . var_export($distribution_entry));
					
					return -3;
				}
				
				$ret = self::QueueRecall($distribution_entry->export_id, $distribution_entry->destination_id);
			}
		}
		
		return $ret;
	}

	static public function QueueRecall($export_id, $destination_id) {
		// get db record
		$rf = self::GetWritableRecordFactory();
		$export_entry = $rf->findInstance('Export', $export_id);
		if (!$export_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Export entry not found: export_id = {$export_id}");
			
			return -1; //Export entry not found
		}
		
		$destination_entry = $rf->findInstance('Destination', $destination_id);
		if (!$destination_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Destination entry not found: destination_id = {$destination_id}");
			
			return -2; //Destination entry
		}
		
		$found = $rf->find('Distribution', 'export_id=? AND destination_id=?', array($export_entry->id, $destination_entry->id));
		if (!$found) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Distribution entry not found: export_id = {$export_id}, destination_id = {$destination_id}");
			
			return -5; //Destination entry
		}
		$distribution_entry = array_shift($found);
		
		$distribution_entry->status = self::STATUS_DELETED;
		$ret = $rf->save_by_fields($distribution_entry, array('export_id', 'destination_id'));
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Mark-Deleted distribution entry record FAILS");
			error_log("Parameters: " . var_export($distribution_entry));
			
			return -3;
		}
				
		// enter distribution queue
		if ($destination_entry->broker) {
			$broker = $destination_entry->broker;
			$queue = $destination_entry->recall_queue;
			$ret = MessageHelper::sendMessage($broker, $queue,
				array('export_id' => $export_id)
			);
				
			if (!$ret) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - sendMessage FAILS");
				error_log("MessageHelper::sendMessage({$broker}, {$queue}, array('export_id' => {$export_id})");
				
				return -4; // Send Message FAILS
			}
		}
		
		return TRUE;
	}
	
	static public function RequestRecall($login_id, $source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $destination_id, $format_id = 1) {
		$rf = self::GetWritableRecordFactory();
		
		if (($width <= 0) || ($height <= 0) || ($source_id <= 0)) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Invalid input parameters FAILS");
			error_log("Parameters: width={$width}, height={$height}, source_id={$source_id}");
			
			return -1;
		}

		// check existing export
		$export_entry = self::GetExport($source_id, $width, $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, $export_arg, $format_id);
		if (!$export_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Export entry not found: source_id = $source_id, width = $width, height = $height, $video_codec, $video_bitrate, $frame_rate, $pixel_aspect_ratio, $audio_codec, $audio_bitrate, $audio_channel, $audio_sample_rate, export_arg = $export_arg");
			
			return -2; //Export entry not found
		}

		$ret = self::QueueRecall($export_entry->id, $destination_id);

		return $export_entry->id;
	}
	
	static public function RecoverSource($source_id) {
		// get db records
		$rf = self::GetWritableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);
	
		if (!$source_entry) {
			return TRUE; // no source is found
		}
		
		$source_entry->status = self::STATUS_ACTIVE;
		$ret = $rf->save($source_entry);
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Recover source entry record FAILS");
			error_log("Parameters: " . var_export($source_entry));
			
			return -1;
		}
		
		
		$export_entry_list = $rf->find('Export', 'source_id=?', array($source_id));
		foreach ($export_entry_list as $export_entry) {
			$export_entry->ready = self::STATUS_ACTIVE;
			$ret = $rf->save($export_entry);
			if (!$ret) {
				error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
				error_log("ERROR - Recover export entry record FAILS");
				error_log("Parameters: " . var_export($export_entry));
				
				return -2;
			}	
		}
		
		return $ret;
	}

	static public function GetDistributedQueueInfo() {
		$config = ConfigReaderFactory::getInstance()->getConfigReader(VideoInfrastructureServerConfig::CONFIG_FILE)->getConfig();
		$broker = $config->distributed->broker;
		$queue = $config->distributed->queue;

		return array($broker, $queue);
	}

	static public function notifyDistributed($export_id, $destination_id) {
		list($broker, $queue) = self::GetDistributedQueueInfo();
		$map = array(
			'action' => 'distributed',
			'export_id' => $export_id,
			'destination_id' => $destination_id,
		);
		$ret = MessageHelper::sendMessage($broker, $queue, $map);
			
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - sendMessage FAILS");
			error_log("MessageHelper::sendMessage({$broker}, {$queue}, array('export_id' => {$export_id}, 'destination_id' => {$destination_id})");
			
			return -1; // Send Message FAILS
		}
		
		return TRUE;
	}
	
	static public function notifyRecalled($export_id, $destination_id) {
		list($broker, $queue) = self::GetDistributedQueueInfo();
		$map = array(
			'action' => 'recalled',
			'export_id' => $export_id,
			'destination_id' => $destination_id,
		);
		$ret = MessageHelper::sendMessage($broker, $queue, $map);
			
		if (!$ret) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - sendMessage FAILS");
			error_log("MessageHelper::sendMessage({$broker}, {$queue}, array('export_id' => {$export_id}, 'destination_id' => {$destination_id})");
			
			return -1; // Send Message FAILS
		}
		
		return TRUE;
	}
/*	
	static public function GetSourceVideoUrl($source_id) {
		$rf = self::GetReadableRecordFactory();
		$source_entry = $rf->findInstance('Source', $source_id);
		if (!$source_entry) {
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - Source entry not found: source_id = {$source_id}");
			
			return -1; // No source record found
		}
		
		$farm = $source_entry->farm;
		$hash = ceil($source_id / 100);		
		$url_prefix = "http://vi.tvb.com/video/$farm/source/$hash";
		$filename = sprintf("%012d.jpg", $source_id);
		
		return "$url_prefix/$filename";
	}
*/
}
	
?>
